package br.ufrj.dcc.xml.dynamicintervals.xpath;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.swing.JTextArea;

import br.ufrj.dcc.xml.database.ItemManager;
import br.ufrj.dcc.xml.dynamicintervals.decoder.StringDecoder;
import br.ufrj.dcc.xml.dynamicintervals.vo.Item;

public class XPath2 {

	
	public static void main(String[] args) {
//		System.out.println(getQuery("/Fumo//Detail/Produtor", 2));
//		System.out.println("\n\n-----\n");
//		System.out.println(getQuery("/Fumo//*/Produtor", 2));
//		System.out.println("\n\n-----\n");
//		System.out.println(getQuery("/Fumo/*/Produtor", 2));
//		System.out.println("\n\n-----\n");
//		System.out.println(getQuery("/Fumo/*/*/Produtor", 2));
//		System.out.println("\n\n-----\n");
//		System.out.println(getQuery("//continent/@name", 1));
//		System.out.println("\n\n-----\n");
//		System.out.println(getQuery("//continent[//@name]", 1));
//		System.out.println("\n\n-----\n");
//		System.out.println(getQuery("//continent[@name]", 1));
//		System.out.println("\n\n-----\n");
		System.out.println(getQuery("//country[@id]//name", 4));
		System.out.println("\n\n-----\n");
//		System.out.println(getQuery("//country//name", 4));
//		System.out.println("\n\n-----\n");
		System.out.println(getQuery("//country[@id='cid-gs-europe-2']//name", 4));
		System.out.println("\n\n-----\n");
		System.out.println(getQuery("//country[@id='cid-gs-europe-2']", 4));
		
		
	
	}
	
	
	public static String runXPath(String path, int idDoc, JTextArea pane) throws IOException, SQLException {
		ItemManager manager = new ItemManager();
		String query=getQuery(path, idDoc);
		ResultSet rsNodes = manager.select(query);
		String result = "";

		while (rsNodes.next()) {
			Item item = new Item();
			item.setData(rsNodes.getString("data"));
			item.setL(rsNodes.getInt("l"));
			item.setR(rsNodes.getInt("r"));
			item.setType(rsNodes.getInt("type"));
			ResultSet rsXMLFragment = manager.selectFullNode(item, idDoc);
			StringDecoder dText = new StringDecoder();
			dText.setKeepFormat(false);
			dText.decodeRS(rsXMLFragment);
			
			result+=dText.getResult() + "\n\n";
		}		
		
		pane.setText(manager.getOutputText());
		
		return result; 
	}	
	
	public static String getQuery(String path, int doc) {
		String query = internalGetQuery( path, doc, 0, false);
		return query;
	}
	
	protected static String internalGetQuery(String path, int doc, int counterOffset, boolean invertePrimeiro) {

		String query = "";
		int tableCounter = counterOffset;
		int closingP = 0;
		
		while(path.trim().length()>0) {
		
			int lastDashDash = path.lastIndexOf("//");
			int lastDash = path.lastIndexOf("/");
			int lastValidDash; // terá a posição do primeiro dash do último trecho (ignorando dashes dentro de filtros) 
			
			int lastOpeningBracket = path.lastIndexOf("[");
			int lastClosingBracket = path.lastIndexOf("]");
			
			boolean parent = true; // true se for /, false se for //
			
			if (lastDashDash>=0 && lastDash-1<=lastDashDash) {
				lastValidDash = lastDashDash;
				parent = false;
			}
			else
				lastValidDash = lastDash;
			
			String filtro = null;
			if (lastValidDash>lastOpeningBracket && lastValidDash<lastClosingBracket) { //na verdade, o (ou os) / estão dentro do filtro
				filtro = path.substring(lastOpeningBracket+1, lastClosingBracket);
				path = path.substring(0, lastOpeningBracket);
				lastDashDash = path.lastIndexOf("//");
				lastDash = path.lastIndexOf("/");
				if (lastDashDash>=0 && lastDash-1<=lastDashDash) {
					lastValidDash = lastDashDash;
					parent = false;
				}
				else
					lastValidDash = lastDash;				
			} else if (lastValidDash<lastOpeningBracket) { // há um filtro sem nenhuma / dentro
				filtro = path.substring(lastOpeningBracket+1, lastClosingBracket);
				path = path.substring(0, lastOpeningBracket);
			}
			
			if (lastValidDash < 0) { // não há mais barras. Se a string não está vazia, tem algo errado.
				if (path.trim().length()<=0)
					return query;
				else {
					/*throw new IllegalArgumentException("Path inválido.");*/ // sem radicalismo :-)
					path = '/' + path;
					lastValidDash=0;
					parent = true;
				}
			}
			
			
			
			String data = path.substring(lastValidDash + (parent ? 1 : 2));
			path = path.substring(0, lastValidDash);
			
			
			String filtrosQuery = generateFiltro(filtro, tableCounter, doc);
			
			
			int tipo = Item.ELEMENT;
			//TODO: Só pode haver atributos no último!
			if (data.charAt(0) == '@') {
				data = data.substring(1);
				tipo = Item.ATTRIBUTE;
			}
			
			tableCounter++;
			boolean inverte = invertePrimeiro && query.trim().length() <= 0;
			
			if (!invertePrimeiro && query.trim().length() <= 0) { //Primeiro
				query = "SELECT i"+tableCounter+".*" +
				" FROM Items i"+tableCounter+" \nWHERE " +
				"i"+tableCounter+".doc = " +doc+" AND " +
				"i"+tableCounter+".type = " +tipo;
				if (!data.equals("*"))
					query += " AND i"+tableCounter+".data = '" +data+"'";
				query += filtrosQuery;
			}
			else {
				closingP++;
				query += " \nAND EXISTS ( ";
				query += "SELECT i"+tableCounter+".l, i"+tableCounter+".r" +
						" FROM Items i"+tableCounter+" \nWHERE " +
						"i"+tableCounter+".doc = " +doc+" AND " +
						"i"+tableCounter+".type = " +tipo+" ";
				if (!data.equals("*"))
					query+="AND i"+tableCounter+".data = '" +data+"'";
				query += filtrosQuery;
				
				
				// Garantir que é um acestral
				query += " AND i"+(tableCounter-1)+".l "+ (inverte?"<":">") +" i"+tableCounter+".l  AND " +
				"i"+(tableCounter-1)+".r "+ (inverte?">":"<") +" i"+tableCounter+".r ";
				
				//As vezes não basta ser ancestral, tem que ser pai
				if (parent) {
					query += " \nAND NOT EXISTS (" +
								"SELECT * " +
								" FROM Items iAux"+tableCounter+" \nWHERE " +
								"iAux"+tableCounter+".doc = " +doc+" AND " +
								"iAux"+tableCounter+".type = " +tipo+" ";

					if (!data.equals("*"))
						query+="AND iAux"+tableCounter+".data = '" +data+"'";
					
					query +=			
								" AND iAux"+tableCounter+".l "+ (inverte?">":"<") +" i"+tableCounter+".l  AND " +
								"iAux"+tableCounter+".r "+ (inverte?"<":">") +" i"+tableCounter+".r " +

								" AND iAux"+tableCounter+".l "+ (inverte?"<":">") +" i"+(tableCounter-1)+".l  AND " +
								"iAux"+tableCounter+".r "+ (inverte?">":"<") +" i"+(tableCounter-1)+".r  " +
							")";
				}
				
			}
		}
		for(int i=0;i<closingP;++i){
			query += " )";				
		}		
		return query;
	}


	private static String generateFiltro(String filtro, int tableCounter, int doc) {
		//Filtro que significa apenas "existe"
		if (filtro == null)
			return "";
		
		String[] xPathOperadores = { "!=", "<=", ">=", "=", "<", ">" }; //Se um operador contém outro, coloque o maior antes
																		// Que tosqueira!
		String[] databaseOperadores = {"<>", "<=", ">=", "=", "<", ">"};
		
		String path=null, operador=null, operando=null;
		for (int i=0;i<xPathOperadores.length; ++i) {
			String s = xPathOperadores[i];
			int pos = filtro.indexOf(s);
			if (pos>=0) {
				path = filtro.substring(0, pos).trim();
				operador = databaseOperadores[i];
				operando = filtro.substring(pos+s.length()).trim();
				break;
			}
		}
		if (path==null)
			path=filtro;
		String pathQuery = internalGetQuery(path, doc, tableCounter+1, true);
		if (operador!=null) {
			pathQuery=pathQuery.substring(0, pathQuery.length()-1);//tirar um )
			pathQuery+=
			" AND EXISTS ( \n" +
			" SELECT * FROM Items iFiltro"+(tableCounter+2) + 
			" WHERE iFiltro"+(tableCounter+2) +".doc = "+doc+" AND " +
			" iFiltro"+(tableCounter+2) +".l>i"+(tableCounter+2) +".l AND iFiltro"+(tableCounter+2) +".r<i"+(tableCounter+2) +".r AND " +
			" (iFiltro"+(tableCounter+2) +".type="+Item.DATA+" OR " +
			" iFiltro"+(tableCounter+2) +".type="+Item.ATTRIBUTEVALUE+") AND " +
			" iFiltro"+(tableCounter+2) +".data " + operador + operando +" AND " + 
			" NOT EXISTS ( \n" +
			" SELECT * FROM Items iFiltroAux"+(tableCounter+2) +" WHERE iFiltroAux"+(tableCounter+2) +".doc = "+doc+" AND " +
						"iFiltroAux"+(tableCounter+2) +".l > i"+(tableCounter+2) +".l AND iFiltroAux"+(tableCounter+2) +".l < iFiltro"+(tableCounter+2) +".l AND " +
						"iFiltroAux"+(tableCounter+2) +".r < i"+(tableCounter+2) +".l AND iFiltroAux"+(tableCounter+2) +".r > iFiltro"+(tableCounter+2) +".r" +
			"\n) )" +
			")";			
			
		}
		return pathQuery;
	}
	
}
